#!/usr/bin/env python
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# Name:        EverSplit.py
# Purpose:     Splits an Evernote 2.2 HTML Export file into a single HTML for
#              each note. It calls the program wkhtmltopdf to generate pdfs
#              with embedded clickable links and sorts them into a directory
#              structure by date and month.
#              The html files are not deleted
#
# Usage:       Script must be run from the command line. Takes the name of the
#              HTML-file as parameter.
#
# Limitations: All Note titles in the exported HTML must be different. Clean
#              up your note titles a little, because they will be used as
#              filnames. The script replaces illegal characters by underscores.
#
# Author:      Ulrich Köhler
#
# History:     2009 created as a quick and dirty script
#              2012 added the wkhtmltopdf tool to convert the html to pdf
# Copyright:   no copyrights
# Licence:     no boring license, do what you will, google forces me
#-------------------------------------------------------------------------------

# imports and parameter checks
import sys, HTMLParser, re, os
if len(sys.argv)<=1:
    print "usage: %s HtmlInputFile" % sys.argv[0]
    sys.exit(1)

# set input filename
ifilename = sys.argv[1]

# This function cleans the note title to be used as a filename.
# Original code fragment grabbed from the internet:
# def encode_as_filename(s):
#     def _(m): return "+%02X" % ord(m.group(0))
#     return re.sub('[\x00"\\\\/*?:<>|+\n]',_,s)
#
# replace all invalid characters by an underscore.
def encode_as_filename(s):
     def _(m): return "_"
     fullname=re.sub('[\x00"\\\\/*?:<>|+\n]',_,s)
     return fullname[:128] # limit filename to 128 chars

# Boolsche Werte
true  = 1
false = 0

# Der Parser wird mit Hilfe einer Zustandsmaschine programmiert.
# Definiere daher verschiedene enums, um sich aktuelle Zustände zu merken.
# In welchem Teil des HTMLCodes befindet sich der Parser.
# html.m_whereami
inNowhere = 0
inNote    = 1
inNotebar = 2
inContent = 3

# Was wurde erkannt und muss als nächstes gelesen werden
# Außer des Titels wird aber nichts weiter in die HTML Dateien
# geschrieben.
# Anmerkung: Metadaten mittels <meta> gehen bei der Konvertierung in ein
# Webarchiv verloren.
# html.m_metadata
getNone  = 0
getTitle = 1
getDate  = 2
getCategories = 3
getAuthor = 4

# simple but pedantic html parser
# Es wird beim Parsen durch die Datei zunächst nur eine Liste der Metadaten inclusive
# der entsprechenden Textpositionen erstellt.
class html(HTMLParser.HTMLParser):

    def __init__(self):
        HTMLParser.HTMLParser.__init__(self)
        self.m_notelist = []
        self.m_noteidx = -1
        self.m_curnote = 0
        self.m_whereami = inNowhere
        self.m_metadata = getNone
        self.in_SPAN = false

    # parse the comments which mark the beginning and end of notes and notebars
    def handle_comment(self, data):
        # split the comment string into single words
        cwords = data.split()
        if cwords[0] == "Begin":
            # get the note number
            self.m_curnote = int(cwords[1].strip('note[]'))
            self.m_whereami = inNote
            print "Beginn der Notiz %d" % self.m_curnote
            self.m_noteidx += 1
            self.m_notelist.append([self.m_curnote, 'title', 'date', [], '', 0, 0])
    	elif cwords[0] == "Notebar":
    		note = int(cwords[1].strip('[]'))
    		if note != self.m_curnote:
    		    print "error"
    		if cwords[2] == "begin":
    		    self.m_whereami = inNotebar
    		    print "Beginn des Notizbalkens %d" % note
    		elif cwords[2] == "end":
    		    self.m_whereami = inNote
    		    print "Ende des Notizbalkens %d" % note
    		else:
    		    print "error"
     	elif cwords[0] == "Note":
    		note = int(cwords[1].strip('[]'))
    		if note != self.m_curnote:
    		    print "error"
    		if cwords[3] == "begin":
    		    self.m_whereami = inContent
    		    print "Beginn des Notizinhalts %d" % note
    		    self.m_notelist[self.m_noteidx][5] = self.getpos()[0]
    		elif cwords[3] == "end":
    		    self.m_whereami = inNote
    		    print "Ende des Notizinhalts %d" % note
    		    self.m_notelist[self.m_noteidx][6] = self.getpos()[0]
    		else:
    		    print "error"
        elif cwords[0] == "End":
            note = int(cwords[1].strip('note[]'))
            if note == self.m_curnote:
                self.m_whereami = inNowhere
                print "Ende der Notiz %d" % self.m_curnote
            else:
                print "error"
        else:
            print "error"

    def handle_starttag(self, tag, attrs):
        if self.m_whereami == inNotebar and tag == "td":
            if len(attrs) != 0:
                if attrs[0][0] == 'id':
                    if attrs[0][1] == "title":
                        self.m_metadata = getTitle
                    elif  attrs[0][1] == "date":
                        self.m_metadata = getDate
                    elif  attrs[0][1] == "categories":
                        self.m_metadata = getCategories
                    elif  attrs[0][1] == "author":
                        self.m_metadata = getAuthor
                    else:
                        self.m_metadata = getNone
        elif tag == "span":
            self.in_SPAN = true


    def handle_endtag(self, tag):
        if self.m_whereami == inNotebar and tag == "td":
            self.m_metadata = getNone
        elif tag == "span":
            self.in_SPAN = false

    def handle_data(self, data):
        if self.m_whereami == inNotebar:
            if self.m_metadata == getTitle:
                #print "Titel: %s" % data
                self.m_notelist[self.m_noteidx][1] = data
            elif self.m_metadata == getDate:
                #print "Datum: %s" % data
                self.m_notelist[self.m_noteidx][2] = data
            elif self.m_metadata == getCategories and self.in_SPAN == true:
                #print "Categories: %s" % data.strip(' ;')
                self.m_notelist[self.m_noteidx][3].append(data.strip(' ;'))
            elif self.m_metadata == getAuthor:
                print "Author %s" % data
                self.m_notelist[self.m_noteidx][4] = data.strip()


htmlfile = open(ifilename, "r")
parser = html()
parser.feed(htmlfile.read())
parser.close()  # force processing all data
htmlfile.close()

htmlhead1 = """\xef\xbb\xbf<HTML>
<HEAD>
"""

htmlhead2 = """<BASEFONT FACE="Verdana" SIZE="2" />
<META http-equiv=Content-Type content="text/html;charset=utf-8" />
<STYLE>
  body, td { font-family: Verdana; font-size: 10pt; }
  #title a, #footer a { color: #304270; text-decoration: underline; }
  #title a:hover, #footer a:hover { color: blue; }
  #title { font-weight: bold; }
  #date { text-align: right; margin-left: 10px; }
  #author { text-align: right; font-style: italic; }
  #auto { font-style: italic; }
  #manual { font-weight: bold; }
  #note { border: 2px solid #cee0f5; }
  #notebar td, #footer td { color: #304270; padding: 2px; padding-left: 4px; padding-right: 4px; background: #cee0f5; font-size: 8pt; }
  #footer td { font-size: 7pt; }
  #download { text-align: right; margin-left: 10px; }
  #body { padding: 15px; }
  #body h1, #body h2, #body h3 { margin-top: 0px; margin-bottom: 10px; }
  #body h1 { font-size: 1.8em; }
  #body h2 { font-size: 1.5em; }
  #body h3 { font-size: 1.2em; }
</STYLE>
</HEAD>
<BODY>
"""

notebar = """<HR><table id="notebar" cellspacing="0" cellpadding="2" width="100%%">
<tr valign="top">
  <td id="title">%s</td>
  <td id="date">%s</td>
</tr>
<tr valign="top">
  <td id="categories"><b>Kategorien:</b> %s </td>
  <td id="author">%s</td>
</tr>
</table>
"""

htmlfile = open(ifilename, "r")
htmllines = htmlfile.readlines()
for note in parser.m_notelist:
    # finally, I got the filname encoding right on Windows.
    ofilename = encode_as_filename(note[1]).decode('utf-8').encode(sys.getfilesystemencoding())
    outfile = open("%s.html" % ofilename, "w")
    outfile.write(htmlhead1)
    outfile.write("<title>%s</title>" % note[1])
    # These metadata is not really used later
    outfile.write("<meta name=\"author\" content=\"%s\">" % note[4])
    outfile.write("<meta name=\"date\" content=\"%s\">" % note[2])
    outfile.write(htmlhead2)
    outfile.writelines(htmllines[note[5]:note[6]])
    catstring = ""
    for s in note[3]:
        catstring += (s + '; ')
    outfile.write(notebar % (note[1], note[2], catstring, note[4]))
    outfile.write("</BODY>")
    outfile.close()
    # create a directory structure for the created pdfs
    # this code is not platform independent!
    targetdir="%s\\%s_%s" % (note[2][6:10], note[2][6:10], note[2][3:5] )
    # grabbed the following fragment from stack overflow website
    try:
        os.makedirs(targetdir)
    except OSError:
        if os.path.exists(targetdir):
            # We are nearly safe
            pass
        else:
            # There was an error on creation, so make sure we know about it
            raise
    # call the pdf conversion
    os.system('wkhtmltopdf\\wkhtmltopdf.exe "%s.html" "%s\\%s.pdf"' % (ofilename, targetdir, ofilename))

htmlfile.close()